﻿package
{
	import org.flixel.FlxG;
	import org.flixel.FlxGame;
	import org.flixel.FlxCore;
	import org.flixel.FlxSprite;
	import org.flixel.FlxState;
	import org.flixel.FlxText;

	
	import Astrosmash;
	import ASPlayer;
	import ASExplode;
	import ASRockLarge;
	import ASRockSmall;
	import ASSpinner;
	import ASSpinnerLarge;
	import ASSpinnerSmall;
	import ASUFO;
	import ASUFOShot;
	
	import caurina.transitions.Tweener;
	
	import com.bit101.components.VUISlider;
	
	import ASBackground;

	public class StateGameLoop extends FlxState
	{
		[Embed(source = 'data/explo.png')] private var animExplode:Class;
		[Embed(source = 'data/creeping_threat.mp3')] private var sndCreepingThreat:Class;
		[Embed(source = 'data/1up.mp3')] private var snd1up:Class;
		
		private const SCORE_X_OFFS:int = 141;
		private const SCORE_Y_OFFS:int = 182;
				
		private var _background:ASBackground;
		
		public var _player:ASPlayer;
		private var _playerShots:Array;
		
		public var _rocksLarge:Array;
		public var _rocksSmall:Array;
		public var _spinnersLarge:Array;
		public var _spinnersSmall:Array;
		public var _seekers:Array;
		public var _ufos:Array;
		public var _ufoShots:Array;
		
		public  var _explosions:Array;
		
		private var _score:int;
		private var _scorePeak:int;
		private var _scoreText:FlxText;
		private var _scorePeakNext1UP:int;
		
		private var _lives:int;
		private var _livesText:FlxText;
		
		private var _level:int;
		private var _levelText:FlxText;
		
		private var _obitText:FlxText;
		
		private var _gameOver:Boolean;
		
		private var _creepingThreatDelay:Delay;
		private var _bg:ASBackground;
						
		/*----------------------------------------------*/
		public function StateGameLoop()
		{
			super();
			
			Tweener.removeAllTweens();
			
			FlxG.showCursor();

			_level = 0;
			_lives = ASF.STARTING_LIVES;
			
			_gameOver = false;
			
			_bg = new ASBackground();
						
			_explosions = new Array;
			for (var i:int = 0; i < 10; i++) {
				_explosions.push( this.add(new ASExplode()) );
			}
			
			_rocksLarge = new Array;
			for (i = 0; i < ASF.MAX_ROCKS_LARGE; i++) {
				_rocksLarge.push( this.add(new ASRockLarge(this)) );
			}
			_rocksSmall = new Array;
			for (i = 0; i < ASF.MAX_ROCKS_SMALL; i++) {
				_rocksSmall.push( this.add(new ASRockSmall(this)) );
			}

			_spinnersLarge = new Array;
			for (i = 0; i < ASF.MAX_SPINNERS_LARGE; i++) {
				_spinnersLarge.push( this.add(new ASSpinnerLarge(this)) );
			}
			_spinnersSmall = new Array;
			for (i = 0; i < ASF.MAX_SPINNERS_SMALL; i++) {
				_spinnersSmall.push( this.add(new ASSpinnerSmall(this)) );
			}
			
			_seekers = new Array;
			for (i = 0; i < ASF.MAX_SEEKERS; i++) {
				_seekers.push( this.add(new ASSeeker(this)) );
			}
			
			_playerShots = new Array;
			for (i = 0; i < ASF.MAX_PLAYER_SHOTS; i++) {
				_playerShots.push( this.add(new ASPlayerShot()) );
			}
			
			_ufos = new Array;
			_ufos.push( this.add(new ASUFO(this)) );

			_ufoShots = new Array;
			_ufoShots.push( this.add(new ASUFOShot()) );
			
			_player = new ASPlayer(_playerShots, this);
			add(_player);
			
						
			_scoreText = this.add(new FlxText(0, SCORE_Y_OFFS, FlxG.width / 2, "")) as FlxText;
			_scoreText.alignment = "right";
			_scoreText.color = 0xFFFFFF;

			_levelText = this.add(new FlxText(0, SCORE_Y_OFFS, FlxG.width, "")) as FlxText;
			_levelText.alignment = "right";
			_levelText.color = 0xFFFFFF;
			
			_livesText = this.add(new FlxText(FlxG.width / 2, SCORE_Y_OFFS, FlxG.width / 2, "")) as FlxText;
			_livesText.color = 0xffffA000;
			_livesText.alignment = "left";

			
			//_livesText.setText(  );
			_livesText.text = "Life: " + _lives;

			_creepingThreatDelay = new Delay(1);
			
			if (FlxG.debug) {
				__debugInit();
			}
			
			reset(true);
		}
		
		/*----------------------------------------------*/
		override public function update():void
		{
			if (FlxG.debug) __debugUpdate();
						
			_scoreText.text = String(_score);
			
			// calculate level multiplier and graphics based on score ----
			
			_level = ASF.getMultiplier(_score);
			_levelText.text = String(_level+1) + "x";
			
			// spawn enemies ---------------------------------------------

			spawnEnemies();
			
			// check for collisions (hope you have insurance) ------------
			if (!_gameOver) {
				// Adjust background color to fit level
								
				collisions();
			
				// check keys ------------------------------------------------
				
				if (FlxG.keys.justPressed("DELETE")) {
					playerKilled();
				}
				
				playCreepingThreat();
			} else {
				// TODO: Need to implement a delay before accepting input
				// so the game over stats screen is seen for several seconds
				
				if (FlxG.keys.justPressed("ENTER")) {
					FlxG.switchState(StateTitleScreen);
				}
			}
			
			super.update();
		}
		
		/*----------------------------------------------*/
		private function collisions():void
		{
			FlxG.overlapArrays(_playerShots, _rocksLarge, playerShotHitRock);
			FlxG.overlapArrays(_playerShots, _rocksSmall, playerShotHitRock);

			FlxG.overlapArrays(_playerShots, _spinnersLarge, playerShotHitSpinner);
			FlxG.overlapArrays(_playerShots, _spinnersSmall, playerShotHitSpinner);

			FlxG.overlapArrays(_playerShots, _seekers, playerShotHitSeeker);

			FlxG.overlapArrays(_playerShots, _ufos, playerShotHitUFO);

			FlxG.overlapArray(_rocksLarge, _player, playerHitByObject);
			FlxG.overlapArray(_rocksSmall, _player, playerHitByObject);
			FlxG.overlapArray(_spinnersLarge, _player, playerHitByObject);
			FlxG.overlapArray(_spinnersSmall, _player, playerHitByObject);
			FlxG.overlapArray(_seekers, _player, playerHitByObject);
			FlxG.overlapArray(_ufoShots, _player, playerHitByObject);
			//FlxG.overlapArray([_ufo], _player, playerHitByObject); // never going to hit, dumbass
		}
		
		/*----------------------------------------------*/
		private function spawnEnemies():void
		{
//return;
			if (Utils.randRange(0, ASF._spawnFreq) < ASF._spawnFreq_Rocks[_level]) {
				
				var rock:ASRock;
				if (int(Math.random()*2)) {
					rock = FlxG.getNonexist(_rocksSmall) as ASRock;
				}
				else {
					rock = FlxG.getNonexist(_rocksLarge) as ASRock;
				}
				if (rock) rock.spawn();
			}
			
			if (Utils.randRange(0, ASF._spawnFreq) < ASF._spawnFreq_Spinners[_level]) {
				var spin:ASSpinner;
				if (int(Math.random()*2)) {
					spin = FlxG.getNonexist(_spinnersSmall) as ASSpinner;
				} else {
					spin = FlxG.getNonexist(_spinnersLarge) as ASSpinner;
				}
				
				if (spin) spin.spawn();
			}
			
			if (Utils.randRange(0, ASF._spawnFreq) < ASF._spawnFreq_Seekers[_level]) {
				var seek:ASSeeker = FlxG.getNonexist(_seekers) as ASSeeker;
				if (seek) seek.spawn();
			}

			
			if (Utils.randRange(0, ASF._spawnFreq) < ASF._spawnFreq_UFO[_level]) {
				var ufo:ASUFO = FlxG.getNonexist(_ufos) as ASUFO;
				if (ufo) ufo.spawn();
			}
		}
					
		/*----------------------------------------------*/
		private function playCreepingThreat():void
		{
			if (_creepingThreatDelay.hasPassed()) {
				FlxG.play(sndCreepingThreat, 2.0);
				_creepingThreatDelay.setDelayLength( 3.0 / (_level+1) );
				_creepingThreatDelay.reset();
			}
		}
		
		/*----------------------------------------------*/
		public function reset(hard:Boolean):void
		{
			if (hard) {
				_score = 0;
			
				_lives = ASF.STARTING_LIVES;
				adjustLives(0); // forces a rewrite of the screen text
			}
			
			for each(var rock:ASRock in _rocksLarge) {
				rock.exists = false;
			}
			for each(rock in _rocksSmall) {
				rock.exists = false;
			}
			for each(var explo:ASExplode in _explosions) {
				explo.exists = false;
			}
			for each(var spin:ASSpinner in _spinnersLarge) {
				spin.exists = false;
			}
			for each(spin in _spinnersSmall) {
				spin.exists = false;
			}
			for each(var shot:ASPlayerShot in _playerShots) {
				shot.exists = false;
			}
			
			_scorePeakNext1UP = ASF.THRESHOLD_1UP;
			_creepingThreatDelay.reset();
		}
		
		/*----------------------------------------------*/
		public function playerShotHitRock(PlayerShot:FlxSprite, Rock:ASRock):void
		{
			Rock.hurt(1);

			var explo:ASExplode = FlxG.getNonexist(_explosions) as ASExplode;
			if (explo)
				explo.spawn(Rock.x, Rock.y);
			
			adjustScore(Rock.getScore());

			PlayerShot.exists = false;
			PlayerShot.visible = false;
		}

		/*----------------------------------------------*/
		public function playerShotHitSeeker(PlayerShot:FlxSprite, Seek:ASSeeker):void
		{
			Seek.hurt(1);

			var explo:ASExplode = FlxG.getNonexist(_explosions) as ASExplode;
			if (explo)
				explo.spawn(Seek.x, Seek.y);
			
			adjustScore(Seek.getScore());

			PlayerShot.exists = false;
			PlayerShot.visible = false;
		}
		
		/*----------------------------------------------*/
		//FIXME: need a unified class for ufo's, spinners, rocks, etc; duplication completely unnecessary here
		public function playerShotHitSpinner(PlayerShot:FlxSprite, Rock:ASSpinner):void
		{
			Rock.hurt(1);

			var explo:ASExplode = FlxG.getNonexist(_explosions) as ASExplode;
			if (explo)
				explo.spawn(Rock.x, Rock.y);
			
			adjustScore(Rock.getScore());

			PlayerShot.exists = false;
			PlayerShot.visible = false;
		}
		/*----------------------------------------------*/
		public function playerShotHitUFO(PlayerShot:FlxSprite, UFO:ASUFO):void
		{
			UFO.hurt(1);

			var explo:ASExplode = FlxG.getNonexist(_explosions) as ASExplode;
			if (explo)
				explo.spawn(UFO.x, UFO.y);
			
			adjustScore(UFO.getScore());

			PlayerShot.exists = false;
			PlayerShot.visible = false;
		}
		
		/*----------------------------------------------*/
		public function playerHitByObject(Rock:FlxSprite, Player:ASPlayer):void // need both args
		{
			playerKilled();
		}
			
		/*----------------------------------------------*/
		public function playerKilled():void
		{
			//TODO: Have more INTV-faithful explosion :P
			
			if ( (!_player.exists) || _player.flickering()) return;
			
			_player.hurt(1);
			
			var explo:ASExplode = FlxG.getNonexist(_explosions) as ASExplode;
			if (explo) explo.spawn(_player.x, _player.y);

			adjustLives( -1);
				
			if (_lives <= 0) {
				_player.exists = false;
				_player.dead = true;
				_player.active = false;
				_player.visible = false;
				FlxG.flash(0xFFFF0000, 3,null,true);
				gameOver();
			} else {
				FlxG.flash(0xFFFF0000, 3, playerKilledFlashDone,true);
			}
			
		}
		
		/*----------------------------------------------*/
		private function playerKilledFlashDone():void
		{
			_player.respawn();
		}
		
		/*----------------------------------------------*/
		private function adjustLives(amount:int):void
		{
			_lives += amount;
			//_livesText.setText("Life: " + String(_lives));
			_livesText.text = "Life: " + String(_lives);
		}
		
		/*----------------------------------------------*/
		public function adjustScore(amount:int):void
		{
			if (_gameOver) return;
			_score += (amount * (_level + 1));
			
			// Have we hit a new high?  Record it.
			if (_score > _scorePeak) {
				_scorePeak = _score;
				if (_scorePeak >= _scorePeakNext1UP) {
					_scorePeakNext1UP += ASF.THRESHOLD_1UP;
					adjustLives(1);
					FlxG.play(snd1up, 6);
				}
			}
		}
				
		/*----------------------------------------------*/
		private function gameOver():void
		{
			//_imgStageColor = null;
			//_imgStageColor = new FlxSprite(null, 0, 0, false, false, FlxG.width, FlxG.height, 0xFFFF0000, false); // bright red death background
			_gameOver = true;
			
			_obitText = new FlxText(0, 0, FlxG.width, "PEAK SCORE: " + _scorePeak);
			_obitText.color = 0xFFFFFF;
			_obitText.size = 16;
			_obitText.alignment = "center";
		}
		
		/*----------------------------------------------*/
		private function gameOverUpdate():void
		{
			//FlxG.switchState(StateTitleScreen);
		}
		
		/*----------------------------------------------*/
		override public function render():void
		{
			_bg.getImage(_score, _gameOver).render();
			
			_scoreText.render();
			_livesText.render();
			_levelText.render();
			
			if (_player.exists && _player.visible) {
				FlxSprite(_player).render();
			}
			
			for each(var shot:ASPlayerShot in _playerShots) {
				if (shot.exists && shot.visible) shot.render();
			}

			for each(var spin:ASSpinner in _spinnersLarge) {
				if (spin.exists && spin.visible) spin.render();
			}
			for each(spin in _spinnersSmall) {
				if (spin.exists && spin.visible) spin.render();
			}
			
			for each(var rock:ASRock in _rocksSmall) {
				if (rock.exists && rock.visible) rock.render();
			}
			
			for each(rock in _rocksLarge) {
				if (rock.exists && rock.visible) rock.render();
			}
			
			for each(var seek:ASSeeker in _seekers) {
				if (seek.exists && seek.visible) seek.render();
			}
			
			for each(var explo:ASExplode in _explosions) {
				if (explo.exists && explo.visible) explo.render();
			}
			
			for each(var ufo:ASUFO in _ufos) {
				if (ufo.exists && ufo.visible) {
					ufo.render();
				}
			}
			for each(var ufoshot:ASUFOShot in _ufoShots) {
				if (ufoshot.exists && ufoshot.visible) {
					ufoshot.render();
				}
			}

			if (_gameOver) {
				// On top of everything
				_obitText.render();
			}
		}

		/*================================*/

		private var vSlider:VUISlider;
		
		private function __debugInit():void
		{
			vSlider = new VUISlider(this, 10, 20);
			vSlider.setSize(20, 160);
			vSlider.setSliderParams(1, 20, 3)
			vSlider.alpha = 0.75;
		}
		
		private function __debugUpdate():void
		{
			if (FlxG.keys.justPressed("ONE"))   {
				_score = 0;
				reset(false);
			}
			if (FlxG.keys.justPressed("TWO"))   {
				_score = ASF._level_thresholds[0] + 1000;
				reset(false);
			}
			if (FlxG.keys.justPressed("THREE")) {
				_score = ASF._level_thresholds[1] + 1000;
				reset(false);
			}
			if (FlxG.keys.justPressed("FOUR"))  {
				_score = ASF._level_thresholds[2] + 1000;
				reset(false);
			}
			if (FlxG.keys.justPressed("FIVE"))  {
				_score = ASF._level_thresholds[3] + 1000;
				reset(false);
			}
			if (FlxG.keys.justPressed("SIX"))   {
				_score = ASF._level_thresholds[4] + 1000;
				reset(false);
			}
			if (FlxG.keys.justPressed("SEVEN")) {
				_score = ASF._level_thresholds[5] + 1000;
				reset(false);
			}
			
			if (FlxG.keys.justPressed("K")) {
				_lives = 1;
				playerKilled();
			}
			
			if (FlxG.keys.justPressed("S")) {
				var ufo:ASUFO = FlxG.getNonexist(_ufos) as ASUFO;
				if (ufo) ufo.spawn();
			}
			
			//_score =
		}
		
		
	}
}